home *** CD-ROM | disk | FTP | other *** search
/ User's Choice Windows CD / User's Choice Windows CD (CMS Software)(1993).iso / misc1 / iv26_w30.zip / SOURCES / TEXTDISP.C < prev    next >
C/C++ Source or Header  |  1991-12-30  |  26KB  |  856 lines

  1. /*
  2.  * Copyright (c) 1987, 1988, 1989 Stanford University
  3.  *
  4.  * Permission to use, copy, modify, distribute, and sell this software and its
  5.  * documentation for any purpose is hereby granted without fee, provided
  6.  * that the above copyright notice appear in all copies and that both that
  7.  * copyright notice and this permission notice appear in supporting
  8.  * documentation, and that the name of Stanford not be used in advertising or
  9.  * publicity pertaining to distribution of the software without specific,
  10.  * written prior permission.  Stanford makes no representations about
  11.  * the suitability of this software for any purpose.  It is provided "as is"
  12.  * without express or implied warranty.
  13.  *
  14.  * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  15.  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
  16.  * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  17.  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
  18.  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  19.  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  20.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  21.  */
  22.  
  23. /*
  24.  * TextDisplay - basic text displaying
  25.  */
  26.  
  27. #include <InterViews/font.h>
  28. #include <InterViews/painter.h>
  29. #include <InterViews/shape.h>
  30. #include <InterViews/textdisplay.h>
  31. #include <string.h>
  32.  
  33. class TextLine {
  34. public:
  35.     TextLine();
  36.     ~TextLine();
  37.  
  38.     void Style(TextDisplay*, int line, int first, int last, int style);
  39.     void AddStyle(TextDisplay*, int line, int first, int last, int style);
  40.     void RemoveStyle(TextDisplay*, int line, int first, int last, int style);
  41.  
  42.     void Insert(TextDisplay*, int line, int index, const char*, int count);
  43.     void Delete(TextDisplay*, int line, int index, int count);
  44.     void Replace(TextDisplay*, int line, const char*, int count);
  45.  
  46.     void Draw(TextDisplay*, int line, int first, int last);
  47.  
  48.     int Index(TextDisplay*, Coord x, boolean between);
  49.     Coord Offset(TextDisplay*, int index);
  50. private:
  51.     void Size(int);
  52.     char* text;
  53.     char* attr;
  54.     int size;
  55.     int lastchar;
  56.     char prefix;
  57.     char postfix;
  58. };
  59.  
  60. TextDisplay::TextDisplay (boolean a) {
  61.     painter = nil;
  62.     canvas = nil;
  63.     autosized = a;
  64.     xmin = 0;
  65.     xmax = 0;
  66.     ymax = 0;
  67.     ymin = 0;
  68.     x0 = 0;
  69.     y0 = 0;
  70.     width = -1;
  71.     lineheight = 1;
  72.     tabwidth = 0;
  73.     firstline = 0;
  74.     lastline = 0;
  75.     topline = 0;
  76.     bottomline = -1;
  77.     widestline = 0;
  78.     lines = nil;
  79.     maxlines = 0;
  80.     Size(firstline, lastline);
  81.     CaretStyle(DefaultCaret);
  82.     Caret(0, 0);
  83. }
  84.  
  85. TextDisplay::~TextDisplay () {
  86.     for (int i = firstline; i <= lastline; ++i) {
  87.         delete Line(i, false);
  88.     }
  89.     delete lines;
  90. }
  91.  
  92. void TextDisplay::Scroll (int line, Coord x, Coord y) {
  93.     while (y < ymax) {
  94.         line -= 1;
  95.         y += lineheight;
  96.     }
  97.     while (y > ymax) {
  98.         line += 1;
  99.         y -= lineheight;
  100.     }
  101.     int yshift = y - Top(line);
  102.     y0 += yshift;
  103.     topline = line;
  104.     bottomline = line + (y - ymin + 1) / lineheight - 1;
  105.     if (canvas != nil && yshift > 0) {
  106.         painter->Copy(
  107.             canvas, xmin, ymin, xmax, ymax-yshift, canvas, xmin, ymin+yshift
  108.         );
  109.         Coord top = Top(topline);
  110.     if (top < ymax) {
  111.         Redraw(xmin, top+1, xmax, ymax);
  112.         }
  113.         Redraw(xmin, ymin, xmax, ymin+yshift-1);
  114.     } else if (canvas != nil && yshift < 0) {
  115.         painter->Copy(
  116.             canvas, xmin, ymin-yshift, xmax, ymax, canvas, xmin, ymin
  117.         );
  118.         Coord bottom = Base(bottomline);
  119.         if (bottom > ymin) {
  120.             Redraw(xmin, ymin, xmax, bottom-1);
  121.         }
  122.         Redraw(xmin, ymax+yshift+1, xmax, ymax);
  123.     }
  124.     int xshift = x - Left(line, 0);
  125.     x0 += xshift;
  126.     if (canvas != nil && xshift > 0) {
  127.         painter->Copy(
  128.             canvas, xmin, ymin, xmax-xshift, ymax, canvas, xmin+xshift, ymin
  129.         );
  130.     Redraw(xmin, ymin, xmin+xshift-1, ymax);
  131.     } else if (canvas != nil && xshift < 0) {
  132.         painter->Copy(
  133.             canvas, xmin-xshift, ymin, xmax, ymax, canvas, xmin, ymin
  134.         );
  135.         Redraw(xmax+xshift+1, ymin, xmax, ymax);
  136.     }
  137. }
  138.     
  139. void TextDisplay::Draw (Painter* p, Canvas* c) {
  140.     painter = p;
  141.     canvas = c;
  142. }
  143.  
  144. void TextDisplay::LineHeight (Coord height) {
  145.     lineheight = height;
  146. }
  147.  
  148. void TextDisplay::TabWidth (Coord width) {
  149.     tabwidth = width;
  150. }
  151.  
  152. void TextDisplay::Resize (Coord xn, Coord yn, Coord xx, Coord yx) {
  153.     xmin = xn;
  154.     ymin = yn;
  155.     xmax = xx;
  156.     ymax = yx;
  157.     bottomline = topline + (ymax+y0 - ymin + 1) / lineheight - 1;
  158. }
  159.  
  160. void TextDisplay::Bounds (Coord& xn, Coord& yn, Coord& xx, Coord& yx) {
  161.     xn = xmin;
  162.     yn = ymin;
  163.     xx = xmax;
  164.     yx = ymax;
  165. }
  166.  
  167. void TextDisplay::Redraw (Coord l, Coord b, Coord r, Coord t) {
  168.     if (canvas != nil) {
  169.         int first = LineNumber(t);
  170.     int last = LineNumber(b);
  171.     for (int i = first; i <= last; ++i) {
  172.             int begin = LineIndex(i, l, false);
  173.             int end = LineIndex(i, r, false);
  174.             TextLine* line = Line(i, false);
  175.             if (line != nil) {
  176.                 line->Draw(this, i, begin, end);
  177.             } else {
  178.                 Coord base = Base(i);
  179.                 Coord top = Top(i);
  180.                 painter->ClearRect(canvas, l, max(base, b), r, min(top, t));
  181.             }
  182.             if (caretline == i && caretindex >= begin && caretindex <= end) {
  183.                 ShowCaret();
  184.             }
  185.         }
  186.     }
  187. }
  188.  
  189. void TextDisplay::Size (int first, int last) {
  190.     if (last - first >= maxlines) {
  191.     int newmaxlines = last - first + 10;
  192.         TextLine** newlines = new TextLine* [newmaxlines];
  193.     memset(newlines, '\0', newmaxlines * sizeof(TextLine*));
  194.     memmove(newlines, lines, maxlines * sizeof(TextLine*));
  195.     delete lines;
  196.     lines = newlines;
  197.     maxlines = newmaxlines;
  198.     }
  199.     if (first < firstline) {
  200.     int count = firstline-first;
  201.     memmove(lines+count, lines, (lastline-firstline+1) * sizeof(TextLine*));
  202.     memset(lines, '\0', count * sizeof(TextLine*));
  203.     }
  204.     firstline = first;
  205.     lastline = last;
  206. }
  207.  
  208. TextLine* TextDisplay::Line (int line, boolean create) {
  209.     if (create) {
  210.     Size(min(firstline, line), max(lastline, line));
  211.     }
  212.     if (line < firstline || line > lastline) {
  213.         return nil;
  214.     } else {
  215.         TextLine* l = lines[Index(line)];
  216.         if (l == nil && create) {
  217.             l = new TextLine();
  218.             lines[Index(line)] = l;
  219.         }
  220.         return l;
  221.     }
  222. }
  223.  
  224. int TextDisplay::Index (int line) {
  225.     return line - firstline;
  226. }
  227.  
  228. void TextDisplay::InsertLinesAfter (int line, int count) {
  229.     if (count > 0) {
  230.     Size(min(firstline, line), max(lastline, line) + count);
  231.     memmove(
  232.         lines + Index(line + 1 + count), lines + Index(line + 1),
  233.         (lastline - line - count) * sizeof(TextLine*)
  234.     );
  235.     memset(lines + Index(line+1), '\0', count * sizeof(TextLine*));
  236.         if (canvas != nil) {
  237.             if (autosized) {
  238.                 ymin = min(ymin, Base(lastline));
  239.                 bottomline = topline + (ymax+y0 - ymin + 1) / lineheight - 1;
  240.             }
  241.             Coord y = Base(line) - 1;
  242.             int shift = count * lineheight;
  243.             painter->Copy(
  244.                 canvas, xmin, ymin + shift, xmax, y, canvas, xmin, ymin
  245.             );
  246.             Coord bottom = Base(bottomline);
  247.             if (bottom > ymin) {
  248.                 Redraw(xmin, ymin, xmax, bottom-1);
  249.             }
  250.         Redraw(xmin, y-shift+1, xmax, y);
  251.     }
  252.     }
  253. }
  254.  
  255. void TextDisplay::InsertLinesBefore (int line, int count) {
  256.     if (count > 0) {
  257.         Size(min(firstline, line) - count, max(lastline, line));
  258.     memmove(
  259.         lines + Index(firstline), lines + Index(firstline + count),
  260.         (line - firstline - count) * sizeof(TextLine*)
  261.         );
  262.     memset(lines + Index(line - count), '\0', count * sizeof(TextLine*));
  263.         if (canvas != nil) {
  264.             if (autosized) {
  265.                 ymax = max(ymax, Top(firstline));
  266.                 topline = bottomline - (ymax+y0 - ymin + 1) / lineheight + 1;
  267.             }
  268.             Coord y = Top(line) + 1;
  269.             int shift = count * lineheight;
  270.         painter->Copy(
  271.         canvas, xmin, y, xmax, ymax-shift, canvas, xmin, y+shift
  272.             );
  273.             Coord top = Top(topline);
  274.             if (top < ymax) {
  275.                 Redraw(xmin, top, xmax, ymax);
  276.             }
  277.             Redraw(xmin, y, xmax, y+shift-1);
  278.         }
  279.     }
  280. }
  281.  
  282. void TextDisplay::DeleteLinesAfter (int line, int count) {
  283.     count = min(count, lastline - line);
  284.     if (count > 0) {
  285.         Size(min(firstline, line), max(lastline, line));
  286.         for (int i = 0; i < count; ++i) {
  287.             delete Line(line+i+1, false);
  288.         }
  289.     memmove(
  290.         lines + Index(line + 1), lines + Index(line + 1 + count),
  291.         (lastline - line - count) * sizeof(TextLine*)
  292.     );
  293.     memset(lines + Index(lastline - count + 1), '\0', count * sizeof(TextLine*));
  294.         if (canvas != nil) {
  295.             Coord y = Base(line) - 1;
  296.             int shift = count * lineheight;
  297.             painter->Copy(
  298.                 canvas, xmin, ymin, xmax, y-shift, canvas, xmin, ymin+shift
  299.             );
  300.             Redraw(xmin, ymin, xmax, ymin+shift-1);
  301.         }
  302.         Size(firstline, lastline - count);
  303.     }
  304. }
  305.  
  306. void TextDisplay::DeleteLinesBefore (int line, int count) {
  307.     count = min(count, line - firstline);
  308.     if (count > 0) {
  309.         Size(min(firstline, line), max(lastline, line));
  310.     for (int i = 0; i < count; ++i) {
  311.         delete Line(line-i-1, false);
  312.         }
  313.     memmove(
  314.         lines + Index(firstline + count), lines + Index(firstline),
  315.         (line - firstline - count) * sizeof(TextLine*)
  316.     );
  317.     memset(lines + Index(firstline), '\0', count * sizeof(TextLine*));
  318.         if (canvas != nil) {
  319.             Coord y = Top(line) + 1;
  320.             int shift = count * lineheight;
  321.             painter->Copy(
  322.                 canvas, xmin, y+shift, xmax, ymax, canvas, xmin, y
  323.             );
  324.             Redraw(xmin, ymax-shift+1, xmax, ymax);
  325.         }
  326.         Size(firstline + count, lastline);
  327.     }
  328. }
  329.  
  330. void TextDisplay::InsertText (int l, int i, const char* t, int c) {
  331.     TextLine* line = Line(l, true);
  332.     line->Insert(this, l, i, t, c);
  333.     if (painter != nil && width != -1) {
  334.         Coord w = line->Offset(this, 10000);
  335.         if (w > width) {
  336.             width = w;
  337.             widestline = l;
  338.         }
  339.     }
  340.     if (autosized) {
  341.         Coord dw = Width() - (xmax - xmin);
  342.         if (dw > 0) {
  343.             xmax += dw;
  344.             Redraw(xmax - dw + 1, ymin, xmax, ymax);
  345.         }
  346.     }
  347.     if (l == caretline) {
  348.         ShowCaret();
  349.     }
  350. }
  351.  
  352. void TextDisplay::DeleteText (int l, int i, int c) {
  353.     TextLine* line = Line(l, true);
  354.     line->Delete(this, l, i, c);
  355.     if (painter != nil && width != -1) {
  356.         if (l == widestline) {
  357.             Coord w = line->Offset(this, 10000);
  358.             if (w < width) {
  359.                 width = -1;
  360.             }
  361.         }
  362.     }
  363.     if (l == caretline) {
  364.         ShowCaret();
  365.     }
  366. }
  367.  
  368. void TextDisplay::ReplaceText (int l, const char* t, int c) {
  369.     TextLine* line = Line(l, true);
  370.     line->Replace(this, l, t, c);
  371.     if (painter != nil && width != -1) {
  372.         Coord w = line->Offset(this, 10000);
  373.         if (w > width) {
  374.             width = w;
  375.             widestline = l;
  376.         } else if (l == widestline && w < width) {
  377.             width = -1;
  378.         }
  379.     }
  380.     if (autosized) {
  381.         Coord dw = Width() - (xmax - xmin);
  382.         if (dw > 0) {
  383.             xmax += dw;
  384.             Redraw(xmax - dw + 1, ymin, xmax, ymax);
  385.         }
  386.     }
  387.     if (l == caretline) {
  388.         ShowCaret();
  389.     }
  390. }
  391.  
  392. void TextDisplay::Style (int l1, int i1, int l2, int i2, int style) {
  393.     for (int l = l1; l <= l2; ++l) {
  394.         int first = (l == l1) ? i1 : -10000;
  395.         int last = (l == l2) ? i2 : 10000;
  396.         Line(l, true)->Style(this, l, first, last, style);
  397.     }
  398.     if (l1 <= caretline && l2 >= caretline) {
  399.         ShowCaret();
  400.     }
  401. }
  402.  
  403. void TextDisplay::AddStyle (int l1, int i1, int l2, int i2, int style) {
  404.     for (int l = l1; l <= l2; ++l) {
  405.         int first = (l == l1) ? i1 : -10000;
  406.         int last = (l == l2) ? i2 : 10000;
  407.         Line(l, true)->AddStyle(this, l, first, last, style);
  408.     }
  409.     if (l1 <= caretline && l2 >= caretline) {
  410.     ShowCaret();
  411.     }
  412. }
  413.  
  414. void TextDisplay::RemoveStyle (int l1, int i1, int l2, int i2, int style) {
  415.     for (int l = l1; l <= l2; ++l) {
  416.         int first = (l == l1) ? i1 : -10000;
  417.         int last = (l == l2) ? i2 : 10000;
  418.         Line(l, true)->RemoveStyle(this, l, first, last, style);
  419.     }
  420.     if (l1 <= caretline && l2 >= caretline) {
  421.         ShowCaret();
  422.     }
  423. }
  424.  
  425. void TextDisplay::CaretStyle (int style) {
  426.     HideCaret();
  427.     caretstyle = style;
  428.     ShowCaret();
  429. }
  430.  
  431. void TextDisplay::Caret (int line, int index) {
  432.     HideCaret();
  433.     caretline = line;
  434.     caretindex = index;
  435.     ShowCaret();
  436. }
  437.  
  438. void TextDisplay::HideCaret () {
  439.     if (canvas != nil && caretline >= topline && caretline <= bottomline) {
  440.         TextLine* l = Line(caretline, true);
  441.         l->Draw(this, caretline, caretindex-1, caretindex);
  442.     }
  443. }
  444.  
  445. void TextDisplay::ShowCaret () {
  446.     if (canvas != nil && caretline >= topline && caretline <= bottomline) {
  447.         Coord l = Left(caretline, caretindex);
  448.         Coord r = Right(caretline, caretindex);
  449.         Coord b = Base(caretline);
  450.     Coord t = Top(caretline);
  451.     if (l >= xmin && r <= xmax) {
  452.             switch (caretstyle) {
  453.             case NoCaret:
  454.                 break;
  455.             case DefaultCaret:
  456.             case BarCaret:
  457.                 painter->Line(canvas, l, b, l, t);
  458.                 break;
  459.             case UnderscoreCaret:
  460.                 painter->FillRect(canvas, l, b, r, b+1);
  461.                 break;
  462.             case OutlineCaret:
  463.                 painter->Rect(canvas, l, b, r, t);
  464.                 break;
  465.             }
  466.         }
  467.     }
  468. }
  469.  
  470. Coord TextDisplay::Width () {
  471.     if (width < 0) {
  472.         if (painter != nil) {
  473.             for (int i = firstline; i <= lastline; ++i) {
  474.                 TextLine* line = Line(i, false);
  475.                 if (line != nil) {
  476.                     width = max(width, line->Offset(this, 10000));
  477.                 }
  478.             }
  479.         }
  480.     }
  481.     return width;
  482. }
  483.  
  484. Coord TextDisplay::Height () {
  485.     return (lastline-firstline + 1) * lineheight;
  486. }
  487.  
  488. int TextDisplay::LineNumber (Coord y) {
  489.     Coord dy = ymax + y0 - y;
  490.     if (dy >= 0) {
  491.     return dy / lineheight;
  492.     } else {
  493.         return - ((-1 - dy) / lineheight + 1);
  494.     }
  495. }
  496.  
  497. int TextDisplay::LineIndex (int line, Coord x, boolean between) {
  498.     TextLine* l = Line(line, false);
  499.     if (l == nil) {
  500.         return 0;
  501.     } else {
  502.         return l->Index(this, x - (xmin + x0), between);
  503.     }
  504. }
  505.  
  506. Coord TextDisplay::Base (int line) {
  507.     return ymax + y0 - line * lineheight - (lineheight - 1);
  508. }
  509.  
  510. Coord TextDisplay::Top (int line) {
  511.     return ymax + y0 - line * lineheight;
  512. }
  513.  
  514. Coord TextDisplay::Left (int line, int index) {
  515.     TextLine* l = Line(line, false);
  516.     if (l == nil) {
  517.         return xmin + x0;
  518.     } else {
  519.         return xmin + x0 + l->Offset(this, index);
  520.     }
  521. }
  522.  
  523. Coord TextDisplay::Right (int line, int index) {
  524.     TextLine* l = Line(line, false);
  525.     if (l == nil) {
  526.         return xmin + x0;
  527.     } else {
  528.         return xmin + x0 + l->Offset(this, index+1) - 1;
  529.     }
  530. }
  531.  
  532. TextLine::TextLine () {
  533.     size = 0;
  534.     text = nil;
  535.     attr = nil;
  536.     prefix = 0;
  537.     postfix = 0;
  538.     lastchar = -1;
  539.     Size(0);
  540. }
  541.  
  542. TextLine::~TextLine () {
  543.     delete text;
  544.     delete attr;
  545. }
  546.  
  547. Coord TextLine::Offset (TextDisplay* display, int index) {
  548.     if (display->painter != nil) {
  549.         Font* f = display->painter->GetFont();
  550.     index = max(0, min(index, lastchar + 1));
  551.     int w = 0;
  552.         int i = 0;
  553.         int cw;
  554.         while (i < index) {
  555.             if (text[i] == '\t') {
  556.                 if (display->tabwidth > 0) {
  557.                     cw = display->tabwidth - w % display->tabwidth;
  558.                 } else {
  559.                     cw = 0;
  560.                 }
  561.             } else {
  562.                 cw = f->Width(text+i, 1);
  563.             }
  564.             w += cw;
  565.             ++i;
  566.         }
  567.         return w;
  568.     } else {
  569.         return 0;
  570.     }
  571. }
  572.  
  573. int TextLine::Index (TextDisplay* display, Coord x, boolean between) {
  574.     if (x < 0) {
  575.         if (!between) {
  576.             return -1;
  577.         } else {
  578.             return 0;
  579.         }
  580.     } else if (display->painter != nil) {
  581.         Font* f = display->painter->GetFont();
  582.         int i = 0;
  583.         int w = 0;
  584.         int cw = 0;
  585.         while (i <= lastchar) {
  586.             if (text[i] == '\t') {
  587.                 if (display->tabwidth > 0) {
  588.                     cw = display->tabwidth - w % display->tabwidth;
  589.                 } else {
  590.             cw = 0;
  591.         }
  592.             } else {
  593.                 cw = f->Width(text+i, 1);
  594.             }
  595.             w += cw;
  596.             if (w > x) {
  597.                 break;
  598.             }
  599.             ++i;
  600.         }
  601.         if (between && i <= lastchar && x > w - cw/2 || !between && x > w) {
  602.             return i+1;
  603.         } else {
  604.             return i;
  605.         }
  606.     } else {
  607.         return 0;
  608.     }
  609. }
  610.  
  611. void TextLine::Size (int last) {
  612.     if (last >= size) {
  613.         int newsize = last<28 ? 28 : last<124 ? 124 : last<1020 ? 1020 : last;
  614.         char* newtext = new char[newsize];
  615.     memset(newtext, '\0', newsize);
  616.     memmove(newtext, text, size);
  617.     delete text;
  618.     text = newtext;
  619.     char* newattr = new char[newsize];
  620.     memset(newattr, '\0', newsize);
  621.     memmove(newattr, attr, size);
  622.         delete attr;
  623.         attr = newattr;
  624.         size = newsize;
  625.     }
  626. }
  627.  
  628. void TextLine::Style (
  629.     TextDisplay* display, int line, int first, int last, int style
  630. ) {
  631.     if (first < 0) {
  632.         prefix = style;
  633.     }
  634.     if (last > lastchar) {
  635.     postfix = style;
  636.     }
  637.     int f = max(first, 0);
  638.     int l = min(last, lastchar);
  639.     for (int i = f; i <= l; ++i) {
  640.         attr[i] = style;
  641.     }
  642.     Draw(display, line, first, last);
  643. }
  644.  
  645. void TextLine::AddStyle (
  646.     TextDisplay* display, int line, int first, int last, int style
  647. ) {
  648.     if (first < 0) {
  649.         prefix = prefix | style;
  650.     }
  651.     if (last > lastchar) {
  652.         postfix = postfix | style;
  653.     }
  654.     int f = max(first, 0);
  655.     int l = min(last, lastchar);
  656.     for (int i = f; i <= l; ++i) {
  657.         attr[i] = attr[i] | style;
  658.     }
  659.     Draw(display, line, first, last);
  660. }
  661.  
  662. void TextLine::RemoveStyle (
  663.     TextDisplay* display, int line, int first, int last, int st
  664. ) {
  665.     if (first < 0) {
  666.         prefix = prefix & ~st;
  667.     }
  668.     if (last > lastchar) {
  669.         postfix = postfix & ~st;
  670.     }
  671.     int f = max(first, 0);
  672.     int l = min(last, lastchar);
  673.     for (int i = f; i <= l; ++i) {
  674.         attr[i] = attr[i] & ~st;
  675.     }
  676.     Draw(display, line, first, last);
  677. }
  678.  
  679. void TextLine::Insert (
  680.     TextDisplay* display, int line, int index, const char* s, int count
  681. ) {
  682.     Coord left, right;
  683.     int shift;
  684.     index = max(0, index);
  685.     Size(max(index, lastchar) + count);
  686.     int src = index;
  687.     int dst = index + count;
  688.     int len = max(0, lastchar - index + 1);
  689.     lastchar += count;
  690.     if (display->canvas != nil) {
  691.     left = display->Left(line, index);
  692.     right = display->Right(line, lastchar+1);
  693.     }
  694.     memmove(text + dst, text + src, len);
  695.     memmove(attr + dst, attr + src, len);
  696.     memmove(text + src, s, count);
  697.     memset(attr + src, '\0', count);
  698.     if (display->canvas != nil) {
  699.         Font* f = display->painter->GetFont();
  700.         if (strchr(text+index, '\t') != nil) {
  701.             Draw(display, line, index, lastchar+1);
  702.         } else {
  703.             shift = display->Left(line, index+count) - left;
  704.             right = min(right, display->xmax - shift);
  705.             if (left <= right) {
  706.                 Coord bottom = display->Base(line);
  707.                 Coord top = bottom + f->Height() - 1;
  708.                 display->painter->Copy(
  709.                     display->canvas, left, bottom, right, top,
  710.             display->canvas, left+shift, bottom
  711.         );
  712.             }
  713.             Draw(display, line, index, index+count-1);
  714.         }
  715.     }
  716. }
  717.  
  718. void TextLine::Delete (
  719.     TextDisplay* display, int line, int index, int count
  720. ) {
  721.     Coord left, right;
  722.     int shift;
  723.     Size(max(lastchar, index));
  724.     count = max(0, min(count, lastchar-index+1));
  725.     int src = index + count;
  726.     int dst = index;
  727.     int len = lastchar - (index + count) + 1;
  728.     if (display->canvas != nil) {
  729.         left = display->Left(line, index + count);
  730.     right = min(display->Right(line, lastchar + 1), display->xmax);
  731.     }
  732.     memmove(text + dst, text + src, len);
  733.     memmove(attr + dst, attr + src, len);
  734.     memset(text + lastchar + 1 - count, '\0', count);
  735.     memset(attr + lastchar + 1 - count, '\0', count);
  736.     lastchar -= count;
  737.     if (display->canvas != nil) {
  738.         if (strchr(text+index, '\t') != nil) {
  739.             Draw(display, line, index, lastchar+1);
  740.         } else {
  741.             shift = left - display->Left(line, index);
  742.             Coord bottom = display->Base(line);
  743.             Coord top = display->Top(line);
  744.             if (left <= right) {
  745.                 display->painter->Copy(
  746.                     display->canvas, left, bottom, right, top,
  747.                     display->canvas, left-shift, bottom
  748.                 );
  749.             }
  750.         if (shift > 0) {
  751.         int last = display->LineIndex(line, right-shift+1, false);
  752.                 Draw(display, line, last, lastchar + 1);
  753.             }
  754.         }
  755.     }
  756. }
  757.  
  758. void TextLine::Replace (
  759.     TextDisplay* display, int line, const char* t, int c
  760. ) {
  761.     delete text;
  762.     text = nil;
  763.     delete attr;
  764.     attr = nil;
  765.     size = 0;
  766.     Size(c);
  767.     prefix = 0;
  768.     postfix = 0;
  769.     lastchar = c - 1;
  770.     memmove(text, t, c);
  771.     memset(attr, '\0', c);
  772.     Draw(display, line, -1, lastchar+1);
  773. }
  774.  
  775. void TextLine::Draw (
  776.     TextDisplay* display, int line, int first, int last
  777. ) {
  778.     if (display->canvas != nil) {
  779.         Font* f = display->painter->GetFont();
  780.         Coord bottom = display->Base(line);
  781.         Coord top = bottom + f->Height() - 1;
  782.         if (line < display->topline || line > display->bottomline) {
  783.             if (top >= display->ymin && bottom <= display->ymax) {
  784.                 display->painter->ClearRect(
  785.                     display->canvas,
  786.                     display->xmin, max(display->ymin, bottom),
  787.                     display->xmax, min(display->ymax, top)
  788.                 );
  789.             }
  790.         } else {
  791.             int start = max(
  792.                 display->LineIndex(line, display->xmin-1, false) + 1,
  793.                 max(0, first)
  794.             );
  795.             int finish = min(
  796.                 display->LineIndex(line, display->xmax+1, false) - 1,
  797.                 min(lastchar, last)
  798.             );
  799.             Coord left = display->Left(line, start);
  800.             Coord right = display->Right(line, finish);
  801.             if (start > first && left > display->xmin) {
  802.                 if ((start>0 ? attr[start-1] : prefix) & Reversed) {
  803.                     display->painter->FillRect(
  804.                         display->canvas, display->xmin, bottom, left-1, top
  805.                     );
  806.                 } else {
  807.                     display->painter->ClearRect(
  808.                         display->canvas, display->xmin, bottom, left-1, top
  809.                     );
  810.                 }
  811.             }
  812.             int done = start;
  813.             display->painter->MoveTo(left, bottom);
  814.             for (int i = start; i <= finish+1; ++i) {
  815.                 if (i==finish+1 || attr[i]!=attr[done] || text[i]=='\t') {
  816.                     if (done != i && text[done] == '\t') {
  817.                         Coord l, r, y;
  818.                         display->painter->GetPosition(l, y);
  819.                         r = display->Right(line, done);
  820.                         if (attr[done] & Reversed) {
  821.                             display->painter->FillRect(
  822.                                 display->canvas, l, bottom, r, top
  823.                             );
  824.                         } else {
  825.                             display->painter->ClearRect(
  826.                                 display->canvas, l, bottom, r, top
  827.                             );
  828.                         }
  829.                         display->painter->MoveTo(r+1, bottom);
  830.                         ++done;
  831.                     }
  832.                     if (done != i) {
  833.                         display->painter->SetStyle(attr[done]);
  834.                         display->painter->Text(
  835.                             display->canvas, text + done, i - done
  836.                         );
  837.                         done = i;
  838.                     }
  839.                 }
  840.             }
  841.             display->painter->SetStyle(Plain);
  842.             if (finish < last && right < display->xmax) {
  843.                 if ((finish<lastchar ? attr[finish+1] : postfix) & Reversed) {
  844.                     display->painter->FillRect(
  845.                         display->canvas, right+1, bottom, display->xmax, top
  846.                     );
  847.                 } else {
  848.                     display->painter->ClearRect(
  849.                         display->canvas, right+1, bottom, display->xmax, top
  850.                     );
  851.                 }
  852.             }
  853.         }
  854.     }
  855. }
  856.